{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2993a7e1",
   "metadata": {},
   "source": [
    "# <center>Deepseek企业级Agent项目开发实战</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ba29c3c",
   "metadata": {},
   "source": [
    "## <center>Part 1. Ollama 本地部署 Deepseek R1 模型</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd836b53",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4e3472e-0460-4322-bbf0-fd496fef9d29",
   "metadata": {},
   "source": [
    "# 1. Ollama项目介绍"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e153236-f15b-4cde-89f3-b803174ac300",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`Ollama`是在`Github`上的一个开源项目，其项目定位是：**一个本地运行大模型的集成框架**，目前主要针对主流的`LLaMA`架构的开源大模型设计，通过将模型权重、配置文件和必要数据封装进由`Modelfile`定义的包中，从而实现大模型的下载、启动和本地运行的自动化部署及推理流程。此外，`Ollama`内置了一系列针对大模型运行和推理的优化策略，目前作为一个非常热门的大模型托管平台，基本主流的大模型应用开发框架如`LangChain`、`AutoGen`、`Microsoft GraphRAG`及热门项目`AnythingLLM`、`OpenWebUI`等高度集成。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48987018-d170-48a7-9663-e9651e0777c2",
   "metadata": {},
   "source": [
    "> `Ollama`通过将大模型运行的所有必要组件（如权重文件、配置设置和相关数据）封装在一个单一的文件或包中，`Modelfile 允许用户更容易地下载、安装、配置和启动模型。这种方法类似于其他软件或应用程序的安装包，它们将所有必要的文件打包在一起，以便用户可以通过简单的安装过程将软件添加到他们的系统中。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4638eea-f1ce-4917-ac79-4aa499e96196",
   "metadata": {},
   "source": [
    "> Ollama官方地址：https://ollama.com/\n",
    "\n",
    "> Ollama Github开源地址：https://github.com/ollama/ollama"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f749114-1063-4686-8433-eed0cc188e8d",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131249751.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c460ff1e-4736-4be7-b3ee-65b4991503c9",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`Ollama`项目支持跨平台部署，目前已兼容<font color='red'>**Mac、Linux和Windows**</font>操作系统。特别地对`Mac`和`Windows`用户提供了非常直观的预览版，包括了内置的`GPU`加速功能、访问完整模型库的能力，以及对`OpenAI`的兼容性在内的`Ollama REST API`，对用户使用尤为友好。\n",
    "\n",
    "&emsp;&emsp;但无论使用哪个操作系统，`Ollama`项目的安装过程都设计得非常简单。根据后续的课程的研发需求以及真实企业的应用需求，我们建议大家使用`Linux`系统进行实践。同时课程也将选择以`Linux`版本为例进行详细介绍。对于其他操作系统版本的安装，大家可以通过如下链接，根据自己的实际情况进行安装体验：https://github.com/ollama/ollama"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "267efd8d-7df6-4781-aa1d-f7e17b0fe7e7",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://snowball101.oss-cn-beijing.aliyuncs.com/img/202403081646978.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f706532b-ff92-4d7a-92c4-a142593b3735",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们重点介绍在Ubuntu 22.04系统下安装部署Ollama项目的详细步骤。具体来说，Ollama在Ubunut系统上的安装方式有两种，分别是：<font color='red'>**一键安装和手动安装**</font>，但**不论使用哪种方法进行安装，都需要安装Ollama项目的服务器上具备网络连通环境**，因为不仅涉及Ollama安装包的更新，还会涉及后续大模型的下载。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8494b889-7290-4c2c-bf09-199ce42ec2c8",
   "metadata": {},
   "source": [
    "# 2. Ollama项目本地安装"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "855b2f05-c6ba-4073-845c-9211bd1a4d72",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`Ollama`项目本地安装的方法极为简单，这里我们以`Linux`系统为例，先进入命令行终端，执行如下一条命令行即可自动化完成：\n",
    "```bash\n",
    "    curl -fsSL https://ollama.com/install.sh | sh\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f3875af-210d-4981-9d57-7e3607e5c2a8",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502111855169.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab4f738c-0f63-4ded-aa47-81a18b8a9c97",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这行命令的目的是从`https://ollama.com/` 网站读取 `install.sh` 脚本，并立即通过 `sh` 执行该脚本，在安装过程中会包含以下几个主要的操作：\n",
    "1. 检查当前服务器的基础环境，如系统版本等；\n",
    "2. 下载Ollama的二进制文件；\n",
    "3. 配置系统服务，包括创建用户和用户组，添加Ollama的配置信息；\n",
    "4. 启动Ollama服务；"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fecec727-bdc9-4c85-9823-6e0e0c003416",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这个过程会比较慢，拉取的文件约2G左右，如果安装过程中未出现任何错误信息，通常情况下能够表明安装已成功。可以通过执行以下命令来检查Ollama服务的运行状态：\n",
    "```bash\n",
    "    systemctl status ollama\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42ff9f91-9d9d-49a8-8ea1-50004b7c6cba",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502111857348.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "770d0623-2ee7-45cd-8f4d-7cc7cf19666b",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如果`Active`状态显示为`active`，则说明Ollama服务目前处于正常运行状态。同时还可以通过以下命令查询当前安装的Ollama版本：\n",
    "```bash\n",
    "    sudo ollama -v\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69b041c4-b3a6-4fd7-a0eb-a522c931dfd0",
   "metadata": {},
   "source": [
    "&emsp;&emsp;**请注意：这种安装方式需要服务器保持联网状态以自动下载`Ollama`的二进制文件。如果出现下述报错，则说明网络环境不通，需要根据实际情况处理网络连接。**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a85cd4c4-ab8c-4ef3-bef5-44615d0e6697",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://snowball101.oss-cn-beijing.aliyuncs.com/img/202403081606383.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a025c949-4297-4e92-91e1-b417578dfe3a",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502111858360.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10961376-85cc-42a1-bbf0-e7da5dddda7d",
   "metadata": {},
   "source": [
    "&emsp;&emsp;至此，我们已成功完成`Ollama`项目的本地部署，并顺利启动了`Ollama`服务。下面，我们将介绍如何开始使用该服务。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "998d88c8-b508-4fb5-b324-65cd72db3e78",
   "metadata": {},
   "source": [
    "# 3. Ollama下载 DeepSeek R1 及启动"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "998b974e-ea12-4e3c-ace8-2aac36e9842f",
   "metadata": {},
   "source": [
    "&emsp;&emsp;需要说明的一点是：`Ollama`项目虽然提供了本地化大模型的能力，但这并不意味着所有大模型都可以通过它下载和使用，其支持的大模型的详细列表可在`Ollama`的官方模型库页面查看：[https://ollama.com/library](https://ollama.com/library)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c74df093-4542-4878-b183-19869e7b24d0",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121017505.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a2dbeb5-20f9-49d4-99f7-e3619c3e8982",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在`Ollama`的模型库中主要支持的还是基于`LLaMA`架构的一些主流大模型，并且现在已经全面接入了`DeepSeek R1`满血版模型及其蒸馏的小模型，可以进入如下页面查看所有可使用的`DeepSeek`模型。注意：`Ollama`暂时没有接入`DeepSeek v3`模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68ba4715",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131352604.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ad19021-99d9-442b-abfa-f55c5dcf8d7a",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在进入到大模型的详细信息页面后，可以通过下拉菜单选择不同参数量的大模型版本。然后需要复制页面右侧提供的模型标识符以进行下一步的模型下载操作。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5dfef704-38a3-4eb9-be99-031b9986f103",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131430011.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8628708-366e-4c69-a663-8734b6106d68",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来回到服务器的命令行终端，直接复制并运行此命令即可执行`Deepseek R1`模型文件的自动化下载，执行的具体命令如下：\n",
    "```bash\n",
    "    ollama run deepseek-r1:32b\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9dfc5b27-3654-436e-817b-02021775e7fc",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131429889.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0315ecc5-c538-4e5f-98eb-4f370f134428",
   "metadata": {},
   "source": [
    "&emsp;&emsp;上述命令会自动执行`deepseek-r1:32b`模型的下载过程，在`Linux`系统中，当下载任务完成后，大模型的全部文件将存储在 `/usr/share/ollama/.ollama/models`路径中，可以通过如下命令进行查看："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acd9005b-e121-4e68-84f9-62ca79ac17e8",
   "metadata": {},
   "source": [
    "> macOS系统路径: ~/.ollama/models\n",
    "\n",
    "> Windows系统: C:\\Users\\%username%\\.ollama\\models"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23826d0c",
   "metadata": {},
   "source": [
    "&emsp;&emsp;同时，进一步进入子文件，即可找到下载模型的具体标识："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e82c1b67-4f05-42ae-bca2-595ad00f916c",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131434757.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e20ec35",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`Ollama` 下载的模型是 `GGUF` 格式。`GGUF`（Generalized Graph Universal Format）是一种用于存储和表示模型的格式。它与原版开源模型的关系是：\n",
    "\n",
    "- 首先下载原版的开源模型（例如这里的 `DeepSeek-R1-Distill-Qwen-32B`）。\n",
    "- 通过转化脚本将原版开源模型被转换为 `GGUF` 格式\n",
    "- 将 `GGUF` 格式的模型文件量化为较低的精度\n",
    "\n",
    "&emsp;&emsp;在 `Ollama` 中，最常用的量化类型是 `Q4_K_M`，表示 `4-bit` 量化，旨在在保持较高性能的同时减少模型的存储需求。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a9b48ad-0e48-464d-a0d6-93e72b9dc463",
   "metadata": {},
   "source": [
    "&emsp;&emsp;此外，还可以使用命令`ollama list`来直接查看通过`Ollama`下载的大模型文件列表，这些模型都支持在线启动和调用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2b3e59a-22e3-4222-9e62-266a9071c0ec",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131436153.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea13c3e9-8208-4a35-a58b-c64db1b65d26",
   "metadata": {},
   "source": [
    "# 4. Ollama启动和使用方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8c6d482-1563-4dca-ad07-773066c29353",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在 `Ollama` 的机制中，使用 `run` 命令时，系统会首先检查本地是否已经存在指定的模型，如果本地没有找到该模型，`Ollama` 会自动执行 `ollama pull <model_name>` 命令，从远程仓库下载该模型，下载完成后将模型存储为 `GGUF` 格式，供后续使用。最后，当成功下载后，`Ollama` 会继续执行 `run` 命令，启动模型并进行推理或生成任务。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd84dd9d-0f2e-4c21-ab75-135f88d6ed85",
   "metadata": {},
   "source": [
    "&emsp;&emsp;因此是可以直接通过在命令行终端对启动的大模型进行调用的，如下所示："
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e7abaf09-ec25-480c-a933-c855d30eb60c",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131629723.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "723d0c59-bce4-4d2b-975e-79e3d3b613f6",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这里要重点说明两点：其一是`DeepSeek R1`作为推理模型，其返回结果是包含<think></think>的，里面包含的是思考推理的内容；其二也会存在<think></think>中为空，这其实是因为`DeepSeek-R1`系列模型倾向于绕过思维模式（即输出” \\ n \\ n ”）,因此一个使用的技巧是：每个输出的开头强制模型以 \"<think>\\n\" 开头。（此问题我们在代码环节在给大家讲解实现的方式）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "988f2cef",
   "metadata": {},
   "source": [
    "# 5. Ollama 多GPU部署及serve启动"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7efd4e85",
   "metadata": {},
   "source": [
    "&emsp;&emsp;使用最简单的命令，即`ollama run xxxx`时，`Ollama`的内部机制会根据启动模型的参数量去运行该模型所需的`VRAM`(显存)。如果该模型可以使用单个`GPU`加载，则`Ollama`将在该`GPU`上加载该模型。这种做法一般可以提供出最佳的性能，因为它可以减少推理过程中`PCI`总线的数据传输量。而如果该模型没办法仅在一个`GPU`上加载，则将分布在所有可用的`GPU`中。比如：\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "349d44a7",
   "metadata": {},
   "source": [
    "&emsp;&emsp;根据官网的介绍，`DeepSeek-r1:32b`模型需要占用`20GB`显存。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ca45da7",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131648407.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "232f6794",
   "metadata": {},
   "source": [
    "&emsp;&emsp;实际也确实运行在了单张`3090 GPU`上，占用约`21GB`显存，如下： "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa85961c",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131648408.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如果想加载多张显卡且做到负载均衡，可以去修改 `ollama` 的`SystemD`配置服务，首先找到当前服务器上`GPU`的 `ID`，执行命令如下：\n",
    "\n",
    "```bash\n",
    "    nvidia-smi\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2555b4db",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131709312.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d0693dc",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如果想加载多张显卡且做到负载均衡，可以去修改 `ollama` 的`SystemD`配置服务，执行如下代码：\n",
    "\n",
    "```bash\n",
    "    systemctl edit ollama.service\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "728af96b",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121127759.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f34d0f6",
   "metadata": {},
   "source": [
    "&emsp;&emsp;编辑并填写如下内容：\n",
    "\n",
    "```bash\n",
    "    Environment=\"CUDA_VISIBLE_DEVICES=0,1,2,3\"    # 这里根据你自己实际的 GPU标号来进行修改\n",
    "    Environment=\"OLLAMA_SCHED_SPREAD=1\"           # 这个参数是做负载均衡\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "433e9e8f",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131711393.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed1db77f",
   "metadata": {},
   "source": [
    "&emsp;&emsp;保存退出后，重新加载`systemd`并重新启动`Ollama`服务使其配置生效，执行如下命令：\n",
    "```bash\n",
    "    systemctl daemon-reload\n",
    "    systemctl restart ollama\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f0c7e7e",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121127761.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d4437a5",
   "metadata": {},
   "source": [
    "&emsp;&emsp;此时再次通过`ollama run xxx` 即可分布式的加载到多张`GPU`显卡上，如下所示："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06a5c498",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131716696.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "291c744e-ff54-48d7-aa02-3a208eef70f3",
   "metadata": {},
   "source": [
    "# 6. Ollama REST API 服务启动及调用"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e5ea09d",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`Ollama run xxx`命令启动模型后，不仅仅是可以在命令行终端与启动的大模型进行对话，更重要的是它还会同步启动`Ollama REST API`，<font color='red'>这个`REST API`服务简单理解：我们可以通过某种方式在代码环境中调用到使用`Ollama`模型启动的大模型，从而和大模型进行对话。</font>默认绑定的 `IP + Port` 是：`http://localhost:11434`，所以，如果启动`Ollama`的服务和当前的代码环境是同一台机器的话，可以使用如下代码进行快速的调用测试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "671d453b-961c-4761-92a7-db46413de0bc",
   "metadata": {},
   "outputs": [
    {
     "ename": "APIConnectionError",
     "evalue": "Connection error.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mRemoteProtocolError\u001b[0m                       Traceback (most recent call last)",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_transports\\default.py:101\u001b[0m, in \u001b[0;36mmap_httpcore_exceptions\u001b[1;34m()\u001b[0m\n\u001b[0;32m    100\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 101\u001b[0m     \u001b[38;5;28;01myield\u001b[39;00m\n\u001b[0;32m    102\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_transports\\default.py:250\u001b[0m, in \u001b[0;36mHTTPTransport.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    249\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m map_httpcore_exceptions():\n\u001b[1;32m--> 250\u001b[0m     resp \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pool\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreq\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    252\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(resp\u001b[38;5;241m.\u001b[39mstream, typing\u001b[38;5;241m.\u001b[39mIterable)\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\connection_pool.py:256\u001b[0m, in \u001b[0;36mConnectionPool.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    255\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_close_connections(closing)\n\u001b[1;32m--> 256\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m exc \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m    258\u001b[0m \u001b[38;5;66;03m# Return the response. Note that in this case we still have to manage\u001b[39;00m\n\u001b[0;32m    259\u001b[0m \u001b[38;5;66;03m# the point at which the response is closed.\u001b[39;00m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\connection_pool.py:236\u001b[0m, in \u001b[0;36mConnectionPool.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    234\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m    235\u001b[0m     \u001b[38;5;66;03m# Send the request on the assigned connection.\u001b[39;00m\n\u001b[1;32m--> 236\u001b[0m     response \u001b[38;5;241m=\u001b[39m \u001b[43mconnection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    237\u001b[0m \u001b[43m        \u001b[49m\u001b[43mpool_request\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\n\u001b[0;32m    238\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    239\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ConnectionNotAvailable:\n\u001b[0;32m    240\u001b[0m     \u001b[38;5;66;03m# In some cases a connection may initially be available to\u001b[39;00m\n\u001b[0;32m    241\u001b[0m     \u001b[38;5;66;03m# handle a request, but then become unavailable.\u001b[39;00m\n\u001b[0;32m    242\u001b[0m     \u001b[38;5;66;03m#\u001b[39;00m\n\u001b[0;32m    243\u001b[0m     \u001b[38;5;66;03m# In this case we clear the connection and try again.\u001b[39;00m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\http_proxy.py:206\u001b[0m, in \u001b[0;36mForwardHTTPConnection.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    199\u001b[0m proxy_request \u001b[38;5;241m=\u001b[39m Request(\n\u001b[0;32m    200\u001b[0m     method\u001b[38;5;241m=\u001b[39mrequest\u001b[38;5;241m.\u001b[39mmethod,\n\u001b[0;32m    201\u001b[0m     url\u001b[38;5;241m=\u001b[39murl,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    204\u001b[0m     extensions\u001b[38;5;241m=\u001b[39mrequest\u001b[38;5;241m.\u001b[39mextensions,\n\u001b[0;32m    205\u001b[0m )\n\u001b[1;32m--> 206\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_connection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43mproxy_request\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\connection.py:103\u001b[0m, in \u001b[0;36mHTTPConnection.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    101\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m exc\n\u001b[1;32m--> 103\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_connection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\http11.py:136\u001b[0m, in \u001b[0;36mHTTP11Connection.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    135\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_response_closed()\n\u001b[1;32m--> 136\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exc\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\http11.py:106\u001b[0m, in \u001b[0;36mHTTP11Connection.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m     97\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m Trace(\n\u001b[0;32m     98\u001b[0m     \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mreceive_response_headers\u001b[39m\u001b[38;5;124m\"\u001b[39m, logger, request, kwargs\n\u001b[0;32m     99\u001b[0m ) \u001b[38;5;28;01mas\u001b[39;00m trace:\n\u001b[0;32m    100\u001b[0m     (\n\u001b[0;32m    101\u001b[0m         http_version,\n\u001b[0;32m    102\u001b[0m         status,\n\u001b[0;32m    103\u001b[0m         reason_phrase,\n\u001b[0;32m    104\u001b[0m         headers,\n\u001b[0;32m    105\u001b[0m         trailing_data,\n\u001b[1;32m--> 106\u001b[0m     ) \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_receive_response_headers\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    107\u001b[0m     trace\u001b[38;5;241m.\u001b[39mreturn_value \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m    108\u001b[0m         http_version,\n\u001b[0;32m    109\u001b[0m         status,\n\u001b[0;32m    110\u001b[0m         reason_phrase,\n\u001b[0;32m    111\u001b[0m         headers,\n\u001b[0;32m    112\u001b[0m     )\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\http11.py:177\u001b[0m, in \u001b[0;36mHTTP11Connection._receive_response_headers\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    176\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[1;32m--> 177\u001b[0m     event \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_receive_event\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    178\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(event, h11\u001b[38;5;241m.\u001b[39mResponse):\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpcore\\_sync\\http11.py:231\u001b[0m, in \u001b[0;36mHTTP11Connection._receive_event\u001b[1;34m(self, timeout)\u001b[0m\n\u001b[0;32m    230\u001b[0m     msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mServer disconnected without sending a response.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m--> 231\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m RemoteProtocolError(msg)\n\u001b[0;32m    233\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_h11_state\u001b[38;5;241m.\u001b[39mreceive_data(data)\n",
      "\u001b[1;31mRemoteProtocolError\u001b[0m: Server disconnected without sending a response.",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[1;31mRemoteProtocolError\u001b[0m                       Traceback (most recent call last)",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1003\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[1;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1002\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m-> 1003\u001b[0m     response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msend\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1004\u001b[0m \u001b[43m        \u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1005\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_should_stream_response_body\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrequest\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1006\u001b[0m \u001b[43m        \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1007\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1008\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m httpx\u001b[38;5;241m.\u001b[39mTimeoutException \u001b[38;5;28;01mas\u001b[39;00m err:\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_client.py:914\u001b[0m, in \u001b[0;36mClient.send\u001b[1;34m(self, request, stream, auth, follow_redirects)\u001b[0m\n\u001b[0;32m    912\u001b[0m auth \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_build_request_auth(request, auth)\n\u001b[1;32m--> 914\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_handling_auth\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    915\u001b[0m \u001b[43m    \u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    916\u001b[0m \u001b[43m    \u001b[49m\u001b[43mauth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mauth\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    917\u001b[0m \u001b[43m    \u001b[49m\u001b[43mfollow_redirects\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfollow_redirects\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    918\u001b[0m \u001b[43m    \u001b[49m\u001b[43mhistory\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    919\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    920\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_client.py:942\u001b[0m, in \u001b[0;36mClient._send_handling_auth\u001b[1;34m(self, request, auth, follow_redirects, history)\u001b[0m\n\u001b[0;32m    941\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[1;32m--> 942\u001b[0m     response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_handling_redirects\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    943\u001b[0m \u001b[43m        \u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    944\u001b[0m \u001b[43m        \u001b[49m\u001b[43mfollow_redirects\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfollow_redirects\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    945\u001b[0m \u001b[43m        \u001b[49m\u001b[43mhistory\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhistory\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    946\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    947\u001b[0m     \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_client.py:979\u001b[0m, in \u001b[0;36mClient._send_handling_redirects\u001b[1;34m(self, request, follow_redirects, history)\u001b[0m\n\u001b[0;32m    977\u001b[0m     hook(request)\n\u001b[1;32m--> 979\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_single_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    980\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_client.py:1014\u001b[0m, in \u001b[0;36mClient._send_single_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m   1013\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m request_context(request\u001b[38;5;241m=\u001b[39mrequest):\n\u001b[1;32m-> 1014\u001b[0m     response \u001b[38;5;241m=\u001b[39m \u001b[43mtransport\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1016\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(response\u001b[38;5;241m.\u001b[39mstream, SyncByteStream)\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_transports\\default.py:249\u001b[0m, in \u001b[0;36mHTTPTransport.handle_request\u001b[1;34m(self, request)\u001b[0m\n\u001b[0;32m    237\u001b[0m req \u001b[38;5;241m=\u001b[39m httpcore\u001b[38;5;241m.\u001b[39mRequest(\n\u001b[0;32m    238\u001b[0m     method\u001b[38;5;241m=\u001b[39mrequest\u001b[38;5;241m.\u001b[39mmethod,\n\u001b[0;32m    239\u001b[0m     url\u001b[38;5;241m=\u001b[39mhttpcore\u001b[38;5;241m.\u001b[39mURL(\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    247\u001b[0m     extensions\u001b[38;5;241m=\u001b[39mrequest\u001b[38;5;241m.\u001b[39mextensions,\n\u001b[0;32m    248\u001b[0m )\n\u001b[1;32m--> 249\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mwith\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mmap_httpcore_exceptions\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[0;32m    250\u001b[0m \u001b[43m    \u001b[49m\u001b[43mresp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pool\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhandle_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreq\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mC:\\Python312\\Lib\\contextlib.py:158\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__exit__\u001b[1;34m(self, typ, value, traceback)\u001b[0m\n\u001b[0;32m    157\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 158\u001b[0m     \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgen\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mthrow\u001b[49m\u001b[43m(\u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    159\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[0;32m    160\u001b[0m     \u001b[38;5;66;03m# Suppress StopIteration *unless* it's the same exception that\u001b[39;00m\n\u001b[0;32m    161\u001b[0m     \u001b[38;5;66;03m# was passed to throw().  This prevents a StopIteration\u001b[39;00m\n\u001b[0;32m    162\u001b[0m     \u001b[38;5;66;03m# raised inside the \"with\" statement from being suppressed.\u001b[39;00m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\httpx\\_transports\\default.py:118\u001b[0m, in \u001b[0;36mmap_httpcore_exceptions\u001b[1;34m()\u001b[0m\n\u001b[0;32m    117\u001b[0m message \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mstr\u001b[39m(exc)\n\u001b[1;32m--> 118\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m mapped_exc(message) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mexc\u001b[39;00m\n",
      "\u001b[1;31mRemoteProtocolError\u001b[0m: Server disconnected without sending a response.",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[1;31mAPIConnectionError\u001b[0m                        Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[1], line 8\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mopenai\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m OpenAI\n\u001b[0;32m      3\u001b[0m client \u001b[38;5;241m=\u001b[39m OpenAI(\n\u001b[0;32m      4\u001b[0m     base_url\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhttp://localhost:11434/v1/\u001b[39m\u001b[38;5;124m'\u001b[39m,      \n\u001b[0;32m      5\u001b[0m     api_key\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mollama\u001b[39m\u001b[38;5;124m'\u001b[39m,  \u001b[38;5;66;03m# 这里随便写，但是api_key字段一定要有\u001b[39;00m\n\u001b[0;32m      6\u001b[0m )\n\u001b[1;32m----> 8\u001b[0m chat_completion \u001b[38;5;241m=\u001b[39m \u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mchat\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcompletions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m      9\u001b[0m \u001b[43m    \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mdeepseek-r1:32b\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m       \u001b[49m\u001b[38;5;66;43;03m# 这里要修改成 你 ollama 启动模型的名称\u001b[39;49;00m\n\u001b[0;32m     10\u001b[0m \u001b[43m    \u001b[49m\u001b[43mmessages\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\n\u001b[0;32m     11\u001b[0m \u001b[43m        \u001b[49m\u001b[43m{\u001b[49m\n\u001b[0;32m     12\u001b[0m \u001b[43m            \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mrole\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43muser\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m     13\u001b[0m \u001b[43m            \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mcontent\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m你好，请你介绍一下你自己\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m     14\u001b[0m \u001b[43m        \u001b[49m\u001b[43m}\u001b[49m\n\u001b[0;32m     15\u001b[0m \u001b[43m    \u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     16\u001b[0m \u001b[43m)\u001b[49m\n\u001b[0;32m     18\u001b[0m \u001b[38;5;28mprint\u001b[39m(chat_completion)\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_utils\\_utils.py:279\u001b[0m, in \u001b[0;36mrequired_args.<locals>.inner.<locals>.wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m    277\u001b[0m             msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMissing required argument: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mquote(missing[\u001b[38;5;241m0\u001b[39m])\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    278\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[1;32m--> 279\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\resources\\chat\\completions.py:863\u001b[0m, in \u001b[0;36mCompletions.create\u001b[1;34m(self, messages, model, audio, frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, metadata, modalities, n, parallel_tool_calls, prediction, presence_penalty, reasoning_effort, response_format, seed, service_tier, stop, store, stream, stream_options, temperature, tool_choice, tools, top_logprobs, top_p, user, extra_headers, extra_query, extra_body, timeout)\u001b[0m\n\u001b[0;32m    821\u001b[0m \u001b[38;5;129m@required_args\u001b[39m([\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m], [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[0;32m    822\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mcreate\u001b[39m(\n\u001b[0;32m    823\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    860\u001b[0m     timeout: \u001b[38;5;28mfloat\u001b[39m \u001b[38;5;241m|\u001b[39m httpx\u001b[38;5;241m.\u001b[39mTimeout \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m|\u001b[39m NotGiven \u001b[38;5;241m=\u001b[39m NOT_GIVEN,\n\u001b[0;32m    861\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ChatCompletion \u001b[38;5;241m|\u001b[39m Stream[ChatCompletionChunk]:\n\u001b[0;32m    862\u001b[0m     validate_response_format(response_format)\n\u001b[1;32m--> 863\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_post\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    864\u001b[0m \u001b[43m        \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m/chat/completions\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m    865\u001b[0m \u001b[43m        \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmaybe_transform\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    866\u001b[0m \u001b[43m            \u001b[49m\u001b[43m{\u001b[49m\n\u001b[0;32m    867\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    868\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmodel\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    869\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43maudio\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43maudio\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    870\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfrequency_penalty\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfrequency_penalty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    871\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfunction_call\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunction_call\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    872\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfunctions\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunctions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    873\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mlogit_bias\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mlogit_bias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    874\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mlogprobs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mlogprobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    875\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmax_completion_tokens\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_completion_tokens\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    876\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmax_tokens\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_tokens\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    877\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmetadata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    878\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmodalities\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodalities\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    879\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mn\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    880\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mparallel_tool_calls\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mparallel_tool_calls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    881\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mprediction\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mprediction\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    882\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpresence_penalty\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpresence_penalty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    883\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mreasoning_effort\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mreasoning_effort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    884\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mresponse_format\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    885\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseed\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    886\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mservice_tier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mservice_tier\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    887\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstop\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    888\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstore\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    889\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstream\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    890\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstream_options\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    891\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtemperature\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    892\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtool_choice\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtool_choice\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    893\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtools\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    894\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtop_logprobs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_logprobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    895\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtop_p\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_p\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    896\u001b[0m \u001b[43m                \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43muser\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43muser\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    897\u001b[0m \u001b[43m            \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    898\u001b[0m \u001b[43m            \u001b[49m\u001b[43mcompletion_create_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCompletionCreateParams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    899\u001b[0m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    900\u001b[0m \u001b[43m        \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmake_request_options\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    901\u001b[0m \u001b[43m            \u001b[49m\u001b[43mextra_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_headers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mextra_query\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_query\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mextra_body\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_body\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\n\u001b[0;32m    902\u001b[0m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    903\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mChatCompletion\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    904\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m    905\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mStream\u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatCompletionChunk\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    906\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1290\u001b[0m, in \u001b[0;36mSyncAPIClient.post\u001b[1;34m(self, path, cast_to, body, options, files, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1276\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mpost\u001b[39m(\n\u001b[0;32m   1277\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m   1278\u001b[0m     path: \u001b[38;5;28mstr\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1285\u001b[0m     stream_cls: \u001b[38;5;28mtype\u001b[39m[_StreamT] \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m   1286\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ResponseT \u001b[38;5;241m|\u001b[39m _StreamT:\n\u001b[0;32m   1287\u001b[0m     opts \u001b[38;5;241m=\u001b[39m FinalRequestOptions\u001b[38;5;241m.\u001b[39mconstruct(\n\u001b[0;32m   1288\u001b[0m         method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpost\u001b[39m\u001b[38;5;124m\"\u001b[39m, url\u001b[38;5;241m=\u001b[39mpath, json_data\u001b[38;5;241m=\u001b[39mbody, files\u001b[38;5;241m=\u001b[39mto_httpx_files(files), \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions\n\u001b[0;32m   1289\u001b[0m     )\n\u001b[1;32m-> 1290\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m cast(ResponseT, \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mopts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m)\u001b[49m)\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:967\u001b[0m, in \u001b[0;36mSyncAPIClient.request\u001b[1;34m(self, cast_to, options, remaining_retries, stream, stream_cls)\u001b[0m\n\u001b[0;32m    964\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    965\u001b[0m     retries_taken \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m--> 967\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    968\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    969\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    970\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    971\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    972\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    973\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1027\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[1;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1024\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEncountered Exception\u001b[39m\u001b[38;5;124m\"\u001b[39m, exc_info\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m   1026\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m remaining_retries \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m-> 1027\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1028\u001b[0m \u001b[43m        \u001b[49m\u001b[43minput_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1029\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1030\u001b[0m \u001b[43m        \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1031\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1032\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1033\u001b[0m \u001b[43m        \u001b[49m\u001b[43mresponse_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m   1034\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1036\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mRaising connection error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m   1037\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m APIConnectionError(request\u001b[38;5;241m=\u001b[39mrequest) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01merr\u001b[39;00m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1105\u001b[0m, in \u001b[0;36mSyncAPIClient._retry_request\u001b[1;34m(self, options, cast_to, retries_taken, response_headers, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1101\u001b[0m \u001b[38;5;66;03m# In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a\u001b[39;00m\n\u001b[0;32m   1102\u001b[0m \u001b[38;5;66;03m# different thread if necessary.\u001b[39;00m\n\u001b[0;32m   1103\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(timeout)\n\u001b[1;32m-> 1105\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1106\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1107\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1108\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1109\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1110\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1111\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1027\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[1;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1024\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEncountered Exception\u001b[39m\u001b[38;5;124m\"\u001b[39m, exc_info\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m   1026\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m remaining_retries \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m-> 1027\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1028\u001b[0m \u001b[43m        \u001b[49m\u001b[43minput_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1029\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1030\u001b[0m \u001b[43m        \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1031\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1032\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1033\u001b[0m \u001b[43m        \u001b[49m\u001b[43mresponse_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m   1034\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1036\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mRaising connection error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m   1037\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m APIConnectionError(request\u001b[38;5;241m=\u001b[39mrequest) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01merr\u001b[39;00m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1105\u001b[0m, in \u001b[0;36mSyncAPIClient._retry_request\u001b[1;34m(self, options, cast_to, retries_taken, response_headers, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1101\u001b[0m \u001b[38;5;66;03m# In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a\u001b[39;00m\n\u001b[0;32m   1102\u001b[0m \u001b[38;5;66;03m# different thread if necessary.\u001b[39;00m\n\u001b[0;32m   1103\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(timeout)\n\u001b[1;32m-> 1105\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1106\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1107\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1108\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1109\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1110\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1111\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32me:\\01_木羽研发\\04_Agent正课\\【项目开发实战】DeepSeek 项目开发\\fufan_deepseek_agent\\.venv\\Lib\\site-packages\\openai\\_base_client.py:1037\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[1;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1027\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_retry_request(\n\u001b[0;32m   1028\u001b[0m             input_options,\n\u001b[0;32m   1029\u001b[0m             cast_to,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1033\u001b[0m             response_headers\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m   1034\u001b[0m         )\n\u001b[0;32m   1036\u001b[0m     log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mRaising connection error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m-> 1037\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m APIConnectionError(request\u001b[38;5;241m=\u001b[39mrequest) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01merr\u001b[39;00m\n\u001b[0;32m   1039\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\n\u001b[0;32m   1040\u001b[0m     \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mHTTP Response: \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%i\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m'\u001b[39m,\n\u001b[0;32m   1041\u001b[0m     request\u001b[38;5;241m.\u001b[39mmethod,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1045\u001b[0m     response\u001b[38;5;241m.\u001b[39mheaders,\n\u001b[0;32m   1046\u001b[0m )\n\u001b[0;32m   1047\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrequest_id: \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m, response\u001b[38;5;241m.\u001b[39mheaders\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mx-request-id\u001b[39m\u001b[38;5;124m\"\u001b[39m))\n",
      "\u001b[1;31mAPIConnectionError\u001b[0m: Connection error."
     ]
    }
   ],
   "source": [
    "from openai import OpenAI\n",
    "\n",
    "client = OpenAI(\n",
    "    base_url='http://localhost:11434/v1/',      \n",
    "    api_key='ollama',  # 这里随便写，但是api_key字段一定要有\n",
    ")\n",
    "\n",
    "chat_completion = client.chat.completions.create(\n",
    "    model='deepseek-r1:32b',       # 这里要修改成 你 ollama 启动模型的名称\n",
    "    messages=[\n",
    "        {\n",
    "            'role': 'user',\n",
    "            'content': '你好，请你介绍一下你自己',\n",
    "        }\n",
    "    ],\n",
    ")\n",
    "\n",
    "print(chat_completion)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1c4adb8-1785-456c-bf11-535e35846fe0",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这里需要注意的一点是：如果 `Ollama` 启动和执行调用的代码是同一台机器，上述代码是可以的跑通的。比如`Ollama`服务在云服务器、局域网的服务器上等情况，则无法通过`http://localhost:11434/v1/` 来进行访问，因为**网络不通**。 正如上述的报错，我的`Ollama`模型服务是在局域网的服务器上，因此我需要修改`Ollama REST API`的请求地址，操作方法如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce2a5c15",
   "metadata": {},
   "source": [
    "&emsp;&emsp;修改 `ollama` 的`SystemD`配置服务，执行如下代码：\n",
    "\n",
    "```bash\n",
    "    systemctl edit ollama.service\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef20cf7d",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121127759.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eaab1e5c",
   "metadata": {},
   "source": [
    "&emsp;&emsp;编辑并填写如下内容：\n",
    "\n",
    "```bash\n",
    "    Environment=\"OLLAMA_HOST=0.0.0.0：11434\"    \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72ee67b8",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121127760.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b32d965",
   "metadata": {},
   "source": [
    "&emsp;&emsp;保存退出后，重新加载`systemd`并重新启动`Ollama`服务使其配置生效，执行如下命令：\n",
    "```bash\n",
    "    systemctl daemon-reload\n",
    "    systemctl restart ollama\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bbde2a6",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121127761.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86230a31",
   "metadata": {},
   "source": [
    "&emsp;&emsp;使用`ollama run xxx`启动模型。然后找到服务器可访问的有效`IP`。在 `Linux` 系统中，可以通过多种方式查看有效的访问 `IP` 地址（即当前与系统建立连接或尝试访问系统的远程 `IP` 地址）。这里使用如下命令：\n",
    "\n",
    "```bash\n",
    "    sudo netstat -tn | grep ESTABLISHED\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d357c3a",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502121127763.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eca30480",
   "metadata": {},
   "source": [
    "&emsp;&emsp;因此，修改访问`Ollama`的`REST API`地址，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a3885e8e-4881-45b0-9303-5d8f45f2d34d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ChatCompletion(id='chatcmpl-309', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='<think>\\n我是DeepSeek-R1，一个由深度求索公司开发的智能助手，我会尽我所能为您提供帮助。\\n</think>\\n\\n我是DeepSeek-R1，一个由深度求索公司开发的智能助手，我会尽我所能为您提供帮助。', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None))], created=1739439431, model='deepseek-r1:32b', object='chat.completion', service_tier=None, system_fingerprint='fp_ollama', usage=CompletionUsage(completion_tokens=53, prompt_tokens=8, total_tokens=61, completion_tokens_details=None, prompt_tokens_details=None))\n"
     ]
    }
   ],
   "source": [
    "from openai import OpenAI\n",
    "\n",
    "client = OpenAI(\n",
    "    base_url='http://192.168.110.131:11434/v1/',     # 这里修改成可访问的 IP\n",
    "    api_key='ollama',   # 这里随便写，但是api_key字段一定要有\n",
    ")\n",
    "\n",
    "chat_completion = client.chat.completions.create(\n",
    "    model='deepseek-r1:32b',\n",
    "    messages=[\n",
    "        {\n",
    "            'role': 'user',\n",
    "            'content': '你好，请你介绍一下你自己',\n",
    "        }\n",
    "    ],\n",
    ")\n",
    "\n",
    "print(chat_completion)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d2bde748",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<think>\n",
      "我是DeepSeek-R1，一个由深度求索公司开发的智能助手，我会尽我所能为您提供帮助。\n",
      "</think>\n",
      "\n",
      "我是DeepSeek-R1，一个由深度求索公司开发的智能助手，我会尽我所能为您提供帮助。\n"
     ]
    }
   ],
   "source": [
    "print(chat_completion.choices[0].message.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "544cc4c0-b8b8-4b11-9611-3f11d963497e",
   "metadata": {},
   "source": [
    "&emsp;&emsp;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fc55f14-23b9-46f4-8e69-62bce8f820dd",
   "metadata": {},
   "source": [
    "&emsp;&emsp;至此，我们就可以像访问大模型`在线API`一样调用本地通过`Ollama`启动的`DeepSeek`模型了。而关于数据隐私问题，因为`Ollama`在本地服务器运行，因此所有的对话数据不会离开机器，大家无需担心隐私数据泄露问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35b8fc09-5809-4ead-a02e-8bf8856900cb",
   "metadata": {},
   "source": [
    "&emsp;&emsp;同时，`Ollama`还有其他的一些常见操作命令，也都非常直观易懂，如下所示："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a639652",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202502131741282.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9136c5e-302d-4c9e-ad4c-1b1c92b7f9d6",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`Ollama`每个命令参数非常容易理解，大家可以自行进行尝试，其参数说明如下所示：\n",
    "\n",
    "<style>\n",
    ".center \n",
    "{\n",
    "  width: auto;\n",
    "  display: table;\n",
    "  margin-left: auto;\n",
    "  margin-right: auto;\n",
    "}\n",
    "</style>\n",
    "\n",
    "<div class=\"center\">\n",
    "\n",
    "| 命令       | 描述                                   |\n",
    "|------------|----------------------------------------|\n",
    "| `serve`    | 启动 Ollama 服务                       |\n",
    "| `create`   | 从 Modelfile 创建一个模型             |\n",
    "| `show`     | 显示模型的信息                         |\n",
    "| `run`      | 运行一个模型                           |\n",
    "| `stop`     | 停止正在运行的模型                     |\n",
    "| `pull`     | 从注册表中拉取一个模型                 |\n",
    "| `push`     | 将一个模型推送到注册表                 |\n",
    "| `list`     | 列出所有模型                           |\n",
    "| `ps`       | 列出正在运行的模型                     |\n",
    "| `cp`       | 复制一个模型                           |\n",
    "| `rm`       | 删除一个模型                           |\n",
    "| `help`     | 显示关于任何命令的帮助信息             |\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ab7eef0-a6a2-492f-834c-bcfcd62fa836",
   "metadata": {},
   "source": [
    "&emsp;&emsp;通过上述关于`Ollama`的安装、模型下载及启动推理的介绍和实践，我们可以感受到`Ollama`极大地简化了大模型部署的过程，也降低了大模型在使用上的技术门槛。然而，对大部分用户而言，命令行界面并不够友好。正如我们之前提到的，在大模型的应用开发框架下，使用到的往往是其`API`调用形式，为此，`Ollama`也是可以集成多个开源项目，包括`Web`界面、桌面应用和终端工具等方式提升使用体验，并满足满足不同用户的偏好和需求。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb212d30-d41c-40cf-b38a-91aee046a317",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来课程的核心会放在`Ollama REST API`的参数介绍及工程化开发技巧上，并在项目功能开发的实际落地场景上，给大家进一步补充`Ollama`的进阶优化技巧。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
